home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgramD2.iso / Borland / Borland C++ V5.02 / CLASSSRC.PAK / THREAD.CPP < prev    next >
C/C++ Source or Header  |  1997-05-06  |  14KB  |  567 lines

  1. //----------------------------------------------------------------------------
  2. // Borland Class Library
  3. // Copyright (c) 1993, 1997 by Borland International, All Rights Reserved
  4. //
  5. //$Revision:   5.15  $
  6. //
  7. // TThread class implementation
  8. //----------------------------------------------------------------------------
  9. #include <classlib/pch.h>
  10. #include <classlib/thread.h>
  11. #include <services/checks.h>
  12. #include <classlib/pointer.h>
  13. #include <process.h>
  14. #include <stdlib.h>
  15.  
  16. DIAG_DEFINE_GROUP(Threads,1,0);
  17.  
  18. //----------------------------------------------------------------------------
  19. // Semaphores Win32
  20. //
  21. #if defined(BI_PLAT_WIN32)
  22.  
  23. //----------------------------------------
  24. // TMutex WIN32
  25.  
  26. void TMutex::Release()
  27. {
  28.   ::ReleaseMutex(*this);
  29. }
  30.  
  31. //----------------------------------------
  32. // TCountedSemaphore WIN32
  33.  
  34. void TCountedSemaphore::Release()
  35. {
  36.   ::ReleaseSemaphore(*this, 1, 0);  
  37. }
  38.  
  39. //----------------------------------------
  40. // TEventSemaphore WIN32
  41.  
  42. void TEventSemaphore::Release()
  43. {
  44.   // Events don't need to be released
  45. }
  46.  
  47. //----------------------------------------
  48. // TSemaphoreSet & its TLock for Win32
  49.  
  50. //
  51. TSemaphoreSet::TSemaphoreSet(const TSemaphore* sems[], int size)
  52. :
  53.   Sems(0)
  54. {
  55.   int count = 0;
  56.   if (sems)
  57.     while (sems[count])
  58.       count++;
  59.   Count = count;
  60.   Size = size >= 0 ? size : count;
  61.   if (Size) {
  62.     Sems = CONST_CAST(const TSemaphore**, new TSemaphorePtr[Size]);
  63.     int i = 0;
  64.     if (sems)
  65.       for (; i < Count; i++)
  66.         Sems[i] = sems[i];
  67.     for (; i < Size; i++)
  68.       Sems[i] = 0;
  69.   }
  70. }
  71.  
  72. //
  73. TSemaphoreSet::~TSemaphoreSet()
  74. {
  75.   delete[] Sems;
  76. }
  77.  
  78. //
  79. void TSemaphoreSet::Add(const TSemaphore& sem)
  80. {
  81.   if (Count < Size)
  82.     Sems[Count++] = &sem;
  83. }
  84.  
  85. //
  86. void TSemaphoreSet::Remove(const TSemaphore& sem)
  87. {
  88.   CHECK(Count <= Size);
  89.   for (int i = 0; i < Count; i++)
  90.     if (Sems[i] == &sem) {
  91.       for (int j = i; j < Count-1; j++)  // Shift rest down to keep packed
  92.         Sems[j] = Sems[j+1];
  93.       Sems[Count-1] = 0;
  94.       return;
  95.     }
  96. }
  97.  
  98. void TSemaphoreSet::Release(int index)
  99. {
  100.   if (index >= 0)
  101.     CONST_CAST(TSemaphore*,Sems[index])->Release();
  102.   else
  103.     for (int i = 0; i < Count; i++)
  104.       CONST_CAST(TSemaphore*,Sems[i])->Release();
  105. }
  106.  
  107. static HANDLE* newHandles(const TSemaphoreSet& set)
  108. {
  109.   HANDLE* handles = new HANDLE[set.GetCount()];
  110.   for (int i = 0; i < set.GetCount(); i++) {
  111.     CHECK(set[i]);
  112.     handles[i] = *set[i];  // Assumes non-0 since i is in set range
  113.   }
  114.   return handles;
  115. }
  116.  
  117. //
  118. TSemaphoreSet::TLock::TLock(const TSemaphoreSet& set, TWaitWhat wait,
  119.                             ulong timeOut, bool alertable)
  120. :
  121.   Set(0)
  122. {
  123.   TAPointer<THandle> handles = newHandles(set);
  124.  
  125.   if (InitLock(set.Count, wait,
  126.                ::WaitForMultipleObjectsEx(set.Count, handles, wait, timeOut, alertable)))
  127.     Set = &set;
  128. }
  129.  
  130. //
  131. TSemaphoreSet::TLock::TLock(ulong msgMask, const TSemaphoreSet& set,
  132.                             TWaitWhat wait, ulong timeOut)
  133. {
  134.   TAPointer<THandle> handles = newHandles(set);
  135.  
  136.   if (InitLock(set.Count, wait,
  137.                ::MsgWaitForMultipleObjects(set.Count, handles, wait, timeOut, msgMask)))
  138.     Set = &set;
  139. }
  140.  
  141. //
  142. // Init the Set and Locked members after a system wait call
  143. //
  144. bool TSemaphoreSet::TLock::InitLock(int count, TWaitWhat wait, int index)
  145. {
  146.   if (index >= WAIT_OBJECT_0 && index < WAIT_OBJECT_0+count ||
  147.       index >= WAIT_ABANDONED_0 && index < WAIT_ABANDONED_0+count)
  148.   {
  149.     if (wait == WaitAny) {
  150.       if (index >= WAIT_ABANDONED_0)
  151.         index -= WAIT_ABANDONED_0;
  152.       Locked = index;      // Just this one is locked
  153.     }
  154.     else
  155.       Locked = AllAquired;         // They are all locked
  156.     return true;
  157.   }
  158.   else if (index == WAIT_OBJECT_0+count)
  159.     Locked = MsgWaiting;
  160.   else if (index == WAIT_TIMEOUT)
  161.     Locked = TimedOut;
  162.   else if (index == WAIT_IO_COMPLETION)
  163.     Locked = IoComplete;
  164.   return false;
  165. }
  166.  
  167. //
  168. TSemaphoreSet::TLock::~TLock()
  169. {
  170.   Release();
  171. }
  172.  
  173. //
  174. void TSemaphoreSet::TLock::Release(bool relinquish)
  175. {
  176.   if (Set) {
  177.     CONST_CAST(TSemaphoreSet*,Set)->Release(Locked);
  178.     if (relinquish)
  179.       Set = 0;
  180.   }
  181. }
  182.  
  183. //----------------------------------------------------------------------------
  184. // Semaphores OS/2
  185. //
  186. #elif defined(BI_PLAT_OS2)
  187.  
  188. #endif
  189.  
  190. //----------------------------------------------------------------------------
  191. // TThread Win32 & OS/2
  192.  
  193. //
  194. // TThread constructors
  195. //
  196. TThread::TThread()
  197. :
  198. #if defined(BI_PLAT_WIN32)
  199.   ThreadId(0),
  200. #elif defined(BI_PLAT_OS2)
  201.   Priority(0),
  202. #endif
  203.   Handle(0),
  204.   Stat(Created),
  205.   TerminationRequested(0),
  206.   Attached(false)
  207. {
  208. }
  209.  
  210. //
  211. // Attach to a running thread
  212. //
  213. TThread::TThread(TCurrent)
  214. :
  215. #if defined(BI_PLAT_WIN32)
  216.   ThreadId(::GetCurrentThreadId()),
  217. #elif defined(BI_PLAT_OS2)
  218.   Priority(0),
  219. #endif
  220.   Handle(0),
  221.   Stat(Running),
  222.   TerminationRequested(0),
  223.   Attached(true)
  224. {
  225.   ::DuplicateHandle(::GetCurrentProcess(), ::GetCurrentThread(),
  226.                     ::GetCurrentProcess(), &Handle,
  227.                     0, false, DUPLICATE_SAME_ACCESS);
  228. }
  229.  
  230. //
  231. //
  232. //
  233. TThread::TThread(const TThread&)
  234. :
  235. #if defined(BI_PLAT_WIN32)
  236.   ThreadId(0),
  237. #elif defined(BI_PLAT_OS2)
  238.   Priority(0),
  239. #endif
  240.   Handle(0),
  241.   Stat(Created),
  242.   TerminationRequested(0),
  243.   Attached(false)
  244. {
  245. }
  246.  
  247. //
  248. // TThread assignment operator
  249. //
  250. // Used when assigning derived objects. Attempting to assign from a running
  251. // object is an error, since the data fields in the running object can be
  252. // changing asynchronously.
  253. //
  254. const TThread& TThread::operator =(const TThread& thread)
  255. {
  256.   switch (GetStatus()) {
  257.     case Created:
  258.     case Suspended:
  259.     case Finished: {
  260.       if (this != &thread) {
  261.         Handle = 0;
  262. #if defined(BI_PLAT_WIN32)
  263.         ThreadId = 0;
  264. #elif defined(BI_PLAT_OS2)
  265.         Priority = 0;
  266. #endif
  267.         Stat = Created;
  268.         TerminationRequested = false;
  269.         Attached = false;
  270.       }
  271.       return *this;
  272.     }
  273.     default:
  274.       throw TThreadError(TThreadError::AssignError);
  275.   }
  276. #if defined(BI_COMP_MSC)
  277.   return *this;     // Bogus return to make MSVC happy
  278. #endif
  279. }
  280.  
  281. //
  282. // TThread destructor
  283. //
  284. // If the thread hasn't finished, destroying its control object is an error.
  285. //
  286. TThread::~TThread()
  287. {
  288.   if (!Attached && (GetStatus() == Running || GetStatus() == Suspended))
  289.     throw TThreadError(TThreadError::DestroyBeforeExit);
  290. #if defined(BI_PLAT_WIN32)
  291. //
  292. // The RTL calls CloseHandle in _endthread, so we shouldn't if the thread
  293. // was started with _beginthreadNT(...).
  294. #  if !defined(BI_MULTI_THREAD_RTL)
  295.   ::CloseHandle(Handle);
  296. #  endif
  297. #elif defined(BI_PLAT_OS2)
  298. #endif
  299. }
  300.  
  301. //
  302. // Starts the thread executing. The actual call depends on the operating system.
  303. // After the system call we check status.
  304. //
  305. TThread::THandle TThread::Start()
  306. {
  307.   // If Start() has already been called for this thread, release the
  308.   // previously created system thread object before launching a new one.
  309.   //
  310.   if ((GetStatus() != Created) && Handle) {
  311. #if defined(BI_PLAT_WIN32)
  312.     ::CloseHandle(Handle);
  313. #elif defined(BI_PLAT_OS2)
  314. #endif
  315.   }
  316.  
  317. #if defined(BI_PLAT_WIN32)
  318. # if defined(BI_MULTI_THREAD_RTL)
  319.   Handle = (HANDLE)::_beginthreadNT(&TThread::Execute, 4096, this, 0, 0, &ThreadId);
  320. # else
  321.   Handle = ::CreateThread(0, 0, &TThread::Execute, this, 0, &ThreadId);
  322. # endif
  323.  
  324. #elif defined(BI_PLAT_OS2)
  325. # if defined(BI_MULTI_THREAD_RTL)
  326.   Handle = ::_beginthread(&TThread::Execute, 4096, this);
  327. # else
  328.   APIRET res =
  329.     ::DosCreateThread(&Handle,
  330.                       (void (__syscall*)(ulong))&TThread::Execute,
  331.                       REINTERPRET_CAST(ulong,this),
  332.                       FALSE,
  333.                       4000);
  334. # endif
  335. #endif
  336.  
  337.   if (Handle) {
  338.     TRACEX(Threads, 1, "Thread started [id:" << Handle << ']');
  339.     Stat = Running;
  340.   }
  341.   else {
  342.     TRACEX(Threads, 2, "Thread failed to start");
  343.     Stat = Invalid;
  344.     throw TThreadError(TThreadError::CreationFailure);
  345.   }
  346.  
  347.   return Handle;
  348. }
  349.  
  350. //
  351. // It's an error to try to suspend a thread that hasn't been started or that
  352. // has already terminated.
  353. //
  354. ulong TThread::Suspend()
  355. {
  356.   switch (GetStatus()) {
  357.     case Created:
  358.       TRACEX(Threads, 2, "Illegal Created thread suspension [id:" << Handle << ']');
  359.       throw TThreadError(TThreadError::SuspendBeforeRun);
  360.     case Finished:
  361.       TRACEX(Threads, 2, "Illegal Finished thread suspension [id:" << Handle << ']');
  362.       throw TThreadError(TThreadError::SuspendAfterExit);
  363.     default:
  364. #if defined(BI_PLAT_WIN32)
  365.       ulong res = ::SuspendThread(Handle);
  366.       if (res < MAXIMUM_SUSPEND_COUNT)  // Else a problem
  367.         Stat = Suspended;
  368. #elif defined(BI_PLAT_OS2)
  369.       ulong res = ::DosSuspendThread(Handle);
  370.       Stat = Suspended;  
  371. #endif
  372.       TRACEX(Threads, 0, "Thread suspended [id:" << Handle << ", Count:" << res << ']');
  373.       return res;
  374.   }
  375. }
  376.  
  377. //
  378. // It's an error to try to resume a thread that isn't suspended.
  379. //
  380. ulong TThread::Resume()
  381. {
  382.   switch (GetStatus()) {
  383.     case Created:
  384.       TRACEX(Threads, 2, "Illegal Created thread resumption [id:" << Handle << ']');
  385.       throw TThreadError(TThreadError::ResumeBeforeRun);
  386.     case Running:
  387.       TRACEX(Threads, 2, "Illegal Running thread resumption [id:" << Handle << ']');
  388.       throw TThreadError(TThreadError::ResumeDuringRun);
  389.     case Finished:
  390.       TRACEX(Threads, 2, "Illegal Finished thread resumption [id:" << Handle << ']');
  391.       throw TThreadError(TThreadError::ResumeAfterExit);
  392.     default:
  393. #if defined(BI_PLAT_WIN32)
  394.       ulong res = ::ResumeThread(Handle);
  395. #elif defined(BI_PLAT_OS2)
  396.       ulong res = ::DosResumeThread(Handle);
  397. #endif
  398.       TRACEX(Threads, 0, "Thread resumed [id:" << Handle << ", Count:" << res << ']');
  399.       if (res == 0)
  400.         Stat = Running;
  401.       return res;
  402.     }
  403. }
  404.  
  405. //
  406. // Mark the thread for termination.
  407. //
  408. void TThread::Terminate()
  409. {
  410.   TRACEX(Threads, 1, "Thread termination requested [handle:" << Handle << ']');
  411.   TerminationRequested = true;
  412. }
  413.  
  414. //
  415. // Block until the thread terminates.
  416. //
  417. // IMPORTANT: the meaning of the 'timeout' parameter is different for NT and
  418. // OS/2. Under NT it specifies how long to wait for termination. Under OS/2 it
  419. // specifies whether to wait or to return immediately if the thread hasn't
  420. // terminated.
  421. //
  422. ulong TThread::WaitForExit(ulong timeout)
  423. {
  424.   TRACEX(Threads, 1, "Waiting for thread exit [id:" << Handle << ']');
  425.   if (Stat == Running)
  426. #if defined(BI_PLAT_WIN32)
  427.     return ::WaitForSingleObject(Handle, timeout);
  428. #elif defined(BI_PLAT_OS2)
  429.     return ::DosWaitThread(&Handle, timeout);
  430. #endif
  431.   else
  432.     return (ulong)-1;
  433. }
  434.  
  435. //
  436. // See note for WaitForExit().
  437. //
  438. ulong TThread::TerminateAndWait(ulong timeout)
  439. {
  440.   Terminate();
  441.   return WaitForExit(timeout);
  442. }
  443.  
  444. //
  445. // Set the thread's priority.
  446. //
  447. int TThread::SetPriority(int pri)
  448. {
  449.   TRACEX(Threads, 1, "Thread priority changed to " << pri <<
  450.                      " [id:" << Handle << ']');
  451. #if defined(BI_PLAT_WIN32)
  452.   return ::SetThreadPriority(Handle, pri);
  453.  
  454. #elif defined(BI_PLAT_OS2)
  455.   APIRET res = ::DosSetPriority(PRTYS_THREAD, PRTYC_NOCHANGE, pri-Priority,
  456.                                 Handle);
  457.   if (res != 0)
  458.     Priority = pri;
  459.   return res;
  460. #endif
  461. }
  462.  
  463. //
  464. //
  465. //
  466. int
  467. TThread::Run()
  468. {
  469.   TRACEX(Threads, 1, "Illegal Run() on base TThread [id:" << Handle << ']');
  470.   return -1;
  471. }
  472.  
  473. //
  474. // Run the thread. This static function is given as the thread start address,
  475. // with 'this' thread object passed as the param. Invoke the Run() method on
  476. // the thread
  477. //
  478. #if defined(BI_MULTI_THREAD_RTL)
  479. void _USERENTRY TThread::Execute(void* thread)
  480. {
  481.   STATIC_CAST(TThread*,thread)->Run();
  482.   STATIC_CAST(TThread*,thread)->Stat = Finished;
  483. }
  484. #elif defined(BI_PLAT_WIN32)
  485. ulong _stdcall TThread::Execute(void* thread)
  486. {
  487.   int code = STATIC_CAST(TThread*,thread)->Run();
  488.   STATIC_CAST(TThread*,thread)->Stat = Finished;
  489.   return code;
  490. }
  491. #elif defined(BI_PLAT_OS2)
  492. void __stdcall TThread::Execute(ulong thread)
  493. {
  494.   REINTERPRET_CAST(TThread*,thread)->Run();
  495.   REINTERPRET_CAST(TThread*,thread)->Stat = Finished;
  496. }
  497. #endif
  498.  
  499. //
  500. // Alternative to returning from Run(). Called from within the thread that
  501. // wants to exit early.
  502. //
  503. void
  504. TThread::Exit(ulong code)
  505. {
  506.   Stat = Finished;
  507. #if defined(BI_PLAT_WIN32)
  508.   ::ExitThread(code);
  509. #elif defined(BI_PLAT_OS2)
  510. #endif
  511. }
  512.  
  513. //
  514. // Call only when Stat claims that the thread is Running.
  515. //
  516. TThread::TStatus
  517. TThread::CheckStatus() const
  518. {
  519. #if defined(BI_PLAT_WIN32)
  520.   uint32 code;
  521.   ::GetExitCodeThread(Handle, &code);
  522.   if (code == STILL_ACTIVE)
  523. #elif defined(BI_PLAT_OS2)
  524.   if (::DosWaitThread(CONST_CAST(THandle*,&Handle), DCWW_NOWAIT) == ERROR_THREAD_NOT_TERMINATED)
  525. #endif
  526.     return Running;
  527.   else
  528.     return Finished;
  529. }
  530.  
  531. //----------------------------------------------------------------------------
  532.  
  533. //
  534. // TThread::TThreadError constructor
  535. //
  536. TThread::TThreadError::TThreadError(TErrorType type)
  537. :
  538.   xmsg(MakeString(type)),
  539.   Type(type)
  540. {
  541. }
  542.  
  543. //
  544. // TThread::TThreadError::MakeString()
  545. //
  546. // Translates an error code into a string.
  547. //
  548. string TThread::TThreadError::MakeString(TErrorType type)
  549. {
  550.   static _TCHAR* Names[] = {
  551.     "Suspend() before Run()",
  552.     "Resume() before Run()",
  553.     "Resume() during Run()",
  554.     "Suspend() after Exit()",
  555.     "Resume() after Exit()",
  556.     "creation failure",
  557.     "destroyed before Exit()",
  558.     "illegal assignment",
  559.     "Multithreaded Runtime not selected",
  560.   };
  561.   string Msg;
  562.   Msg.reserve(40);
  563.   Msg = "Error[thread]: ";
  564.   Msg += Names[type];
  565.   return Msg;
  566. }
  567.